﻿using System;
using System.Windows;

namespace Microsoft.Popfly.GameCreator.GameEngine.Behaviors
{
    public static class MotionBehaviorFactory
    {
        public static IBehavior CreateMotionBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            switch (motionInfo.ContinueMoving)
            {
                case Data.MotionInfo.ContinueMovingValues.WhileReceiving :
                    return CreateWhileReceivingMotionBehavior(game, owner, motionInfo); 
                case Data.MotionInfo.ContinueMovingValues.Forever:
                    {
                        Data.MotionInfo simpleMotionInfo = motionInfo.Copy();
                        simpleMotionInfo.ContinueMoving = Data.MotionInfo.ContinueMovingValues.WhileReceiving;
                        IBehavior behavior = CreateMotionBehavior(game, owner, simpleMotionInfo);

                        return new ContinuedBehavior(game, owner, behavior);
                    }

                case Data.MotionInfo.ContinueMovingValues.ForDuration:
                    {
                        Data.MotionInfo simpleMotionInfo = motionInfo.Copy();
                        simpleMotionInfo.ContinueMoving = Data.MotionInfo.ContinueMovingValues.WhileReceiving;
                        MotionBehavior behaviorToTime = CreateWhileReceivingMotionBehavior(game, owner, simpleMotionInfo);

                        if (motionInfo.AutoReverse)
                        {
                            if (motionInfo.Repeat == "Forever")
                            {
                                return new TimedReverseForeverBehavior(game, owner, behaviorToTime, motionInfo.Duration);
                            }
                            else
                            {
                                return new TimedReverseOnceBehavior(game, owner, behaviorToTime, motionInfo.Duration);
                            }
                        }
                        else
                        {
                            return new TimedBehavior(game, owner, behaviorToTime, motionInfo.Duration);
                        }
                    }
                case Data.MotionInfo.ContinueMovingValues.ForDistance:
                    {
                        Data.MotionInfo simpleMotionInfo = motionInfo.Copy();
                        simpleMotionInfo.ContinueMoving = Data.MotionInfo.ContinueMovingValues.WhileReceiving;
                        MotionBehavior behaviorToTest = CreateWhileReceivingMotionBehavior(game, owner, simpleMotionInfo);

                        if (motionInfo.AutoReverse)
                        {
                            if (motionInfo.Repeat == "Forever")
                            {
                                return new DistancedReverseForeverBehavior(game, owner, behaviorToTest, motionInfo.Distance);
                            }
                            else
                            {
                                return new DistancedReverseOnceBehavior(game, owner, behaviorToTest, motionInfo.Distance);
                            }
                        }
                        else
                        {
                                return new DistancedBehavior(game, owner, behaviorToTest, motionInfo.Distance);
                        }
                    }
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "We have only implemented motions that last Forever, while receving event, for a duration or for a distance.  " +
                        "The value passed in for 'ContinueMoving' was '" + motionInfo.ContinueMoving.ToString() +
                        "'."
                        );
            }
        }

        private static MotionBehavior CreateWhileReceivingMotionBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Relative)
            {
                case Data.MotionInfo.RelativeValues.Scene:
                    switch (motionInfo.Type)
                    {
                        case "Move":
                            return CreateSimpleMotionBehavior(owner, motionInfo);
                        case "Rotate":
                            return CreateRotateSceneBehavior(game, owner, motionInfo);
                        default:
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix +
                                "We have only implemented motions that modify Movement and Rotation.  " +
                                "A move type of '" + motionInfo.Type + "' was passed in."
                                );
                    }
                case Data.MotionInfo.RelativeValues.Actor:
                    switch (motionInfo.Type)
                    {
                        case "Move":
                            switch (motionInfo.SubType)
                            {
                                case "actorInstance":
                                    return CreateActorInstanceRelativeMoveBehavior(game, owner, motionInfo);
                                case "actor":
                                    return CreateActorRelativeMoveBehavior(game, owner, motionInfo);
                                default:
                                    throw new NotImplementedException(
                                        Utils.ErrorMessagePrefix +
                                        "Don't know how to handle a motion with a Relative value of Actor and a subtype of '" +
                                        motionInfo.SubType + "'.");
                            }
                        case "Rotate":
                            switch (motionInfo.SubType)
                            {
                                case "actorInstance":
                                    return CreateActorInstanceRelativeRotationBehavior(game, owner, motionInfo);
                                case "actor":
                                    return CreateActorRelativeRotationBehavior(game, owner, motionInfo);
                                default:
                                    throw new NotImplementedException(
                                        Utils.ErrorMessagePrefix +
                                        "Don't know how to handle a motion with a Relative value of Actor and a subtype of '" +
                                        motionInfo.SubType + "'.");
                            }
                        default:
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix +
                                "We have only implemented motions that modify Movement and Rotation.  " +
                                "A move type of '" + motionInfo.Type + "' was passed in."
                                );
                    }

                case Data.MotionInfo.RelativeValues.Mouse:
                    switch (motionInfo.Type)
                    {
                        case "Move":
                            return CreateMouseRelativeMoveBehavior(game, owner, motionInfo);
                        case "Rotate":
                            return CreateMouseRelativeRotationBehavior(game, owner, motionInfo);
                        default:
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix +
                                "We have only implemented motions that modify Movement and Rotation.  " +
                                "A move type of '" + motionInfo.Type + "' was passed in."
                                );
                    }

                case Data.MotionInfo.RelativeValues.Point:
                    switch (motionInfo.Type)
                    {
                        case "Move":
                            return CreatePointRelativeMoveBehavior(game, owner, motionInfo);
                        case "Rotate":
                            return CreatePointRelativeRotationBehavior(game, owner, motionInfo);
                        default:
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix +
                                "We have only implemented motions that modify Movement and Rotation.  " +
                                "A move type of '" + motionInfo.Type + "' was passed in."
                                );
                    }

                case Data.MotionInfo.RelativeValues.Self:
                    switch (motionInfo.Type)
                    {
                        case "Move":
                            return CreateMoveRelativeToSelf(game, owner, motionInfo);
                        case "Rotate":
                            return CreateSimpleMotionBehavior(owner, motionInfo);
                        default:
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix +
                                "We have only implemented motions that modify Movement and Rotation.  " +
                                "A move type of '" + motionInfo.Type + "' was passed in."
                                );
                    }
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "We have only implemented motions relative to Scene, Actor, Mouse, Point or Self.  " +
                        "The value passed in for 'Relative' was '" + motionInfo.Relative.ToString() +
                        "'."
                        );
            }
        }

        private static MotionBehavior CreateRotateSceneBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            double radians = ConvertDirectionToRadians(motionInfo.Direction);
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationRotateTowardRadians(game, owner, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityRotateTowardRadians(game, owner, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedRotateTowardRadians(game, owner, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreatePointRelativeRotationBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            Point to = new Point(motionInfo.TargetPointX, motionInfo.TargetPointY);
            double radians = ConvertDirectionToRadians(motionInfo.Direction);

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationRotateTowardPoint(game, owner, to, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityRotateTowardPoint(game, owner, to, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedRotateTowardPoint(game, owner, to, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMouseRelativeRotationBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            double radians = ConvertDirectionToRadians(motionInfo.Direction);

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationRotateTowardMouse(game, owner, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityRotateTowardMouse(game, owner, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedRotateTowardMouse(game, owner, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateActorRelativeRotationBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            string actorName = motionInfo.Includes.getIndexedKey(0);
            if (actorName == null)
            {
                throw new ArgumentException("To move relative to an actor, an actor name must be specified");
            }
            double radians;
            // Compat hack, if not defined, the direction is N
            if (motionInfo.Direction == Data.MotionInfo.DirectionValues.NotDefined)
            {
                radians = ConvertDirectionToRadians(Data.MotionInfo.DirectionValues.N);
            }
            else
            {
                radians = ConvertDirectionToRadians(motionInfo.Direction);
            }

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationRotateTowardActor(game, owner, actorName, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityRotateTowardActor(game, owner, actorName, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedRotateTowardActor(game, owner, actorName, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateActorInstanceRelativeRotationBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            Actor actor = owner.Scene.GetActor(motionInfo.Includes.getIndexedKey(0));

            double radians;
            // Compat hack, if not defined, the direction is N
            if (motionInfo.Direction == Data.MotionInfo.DirectionValues.NotDefined)
            {
                radians = ConvertDirectionToRadians(Data.MotionInfo.DirectionValues.N);
            }
            else
            {
                radians = ConvertDirectionToRadians(motionInfo.Direction);
            }

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationRotateTowardActorInstance(game, owner, actor, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityRotateTowardActorInstance(game, owner, actor, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedRotateTowardActorInstance(game, owner, actor, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveRelativeToSelf(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            double radians = ConvertDirectionToRadians(motionInfo.Direction);

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new AccelerationMoveRelativeToSelf(owner, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new VelocityMoveRelativeToSelf(owner, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MaxSpeedMoveRelativeToSelf(owner, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static double ConvertDirectionToRadians(Data.MotionInfo.DirectionValues direction)
        {
            switch (direction)
            {

                case Data.MotionInfo.DirectionValues.N:
                    return 0;
                case Data.MotionInfo.DirectionValues.S:
                    return Math.PI;
                case Data.MotionInfo.DirectionValues.W:
                    return -Math.PI / 2;
                case Data.MotionInfo.DirectionValues.E:
                    return Math.PI / 2;
                case Data.MotionInfo.DirectionValues.NE:
                    return Math.PI / 4;
                case Data.MotionInfo.DirectionValues.SE:
                    return Math.PI * 3 / 4;
                case Data.MotionInfo.DirectionValues.SW:
                    return -Math.PI * 3 / 4;
                case Data.MotionInfo.DirectionValues.NW:
                    return -Math.PI / 4;
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know what radians correspond to a direction of '" +
                        direction.ToString() +
                        "'.");
            }
        }

        private static MotionBehavior CreatePointRelativeMoveBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            bool callReverse = false;
            MotionBehavior behavior;

            switch (motionInfo.Direction)
            {
                case Data.MotionInfo.DirectionValues.S:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.N;
                case Data.MotionInfo.DirectionValues.N:
                    behavior = CreateMoveTowardPointBehavior(game, owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.W:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.E;
                case Data.MotionInfo.DirectionValues.E:
                    behavior = CreateMoveRightFromPointBehavior(owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.NE :
                case Data.MotionInfo.DirectionValues.SE:
                case Data.MotionInfo.DirectionValues.SW :
                case Data.MotionInfo.DirectionValues.NW :
                    behavior = CreateMoveAngleFromPointBehavior(game, owner, motionInfo, ConvertDirectionToRadians(motionInfo.Direction));
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a motion direction of '" + motionInfo.Direction.ToString() + "'.");
            }

            if (callReverse)
            {
                behavior.Reverse();
            }
            return behavior;
        }

        private static MotionBehavior CreateMoveAngleFromPointBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo, double radians)
        {
            Point target = new Point(motionInfo.TargetPointX, motionInfo.TargetPointY);

            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveAngleFromPointAccelerationBehavior(game, owner, target, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveAngleFromPointVelocityBehavior(game, owner, target, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveAngleFromPointMaxSpeedBehavior(game, owner, target, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveRightFromPointBehavior(BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            Point target = new Point(motionInfo.TargetPointX, motionInfo.TargetPointY);
            
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveRightFromPointAccelerationBehavior(owner, target, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveRightFromPointVelocityBehavior(owner, target, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveRightFromPointMaxSpeedBehavior(owner, target, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveTowardPointBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            Point target = new Point(motionInfo.TargetPointX, motionInfo.TargetPointY);
            
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardPointAccelerationBehavior(game, owner, target, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardPointVelocityBehavior(game, owner, target, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardPointMaxSpeedBehavior(game, owner, target, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMouseRelativeMoveBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            bool callReverse = false;
            MotionBehavior behavior;

            switch (motionInfo.Direction)
            {
                case Data.MotionInfo.DirectionValues.S:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.N;
                case Data.MotionInfo.DirectionValues.N:
                    if (motionInfo.IgnoreMouseX)
                    {
                        behavior = CreateMoveTowardMouseYBehavior(game, owner, motionInfo);
                    }
                    else if (motionInfo.IgnoreMouseY)
                    {
                        behavior = CreateMoveTowardMouseXBehavior(game, owner, motionInfo);
                    }
                    else
                    {
                        behavior = CreateMoveTowardMouseBehavior(game, owner, motionInfo);
                    }
                    break;
                case Data.MotionInfo.DirectionValues.W:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.E;
                case Data.MotionInfo.DirectionValues.E:
                    behavior = CreateMoveRightFromMouseBehavior(game, owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.NE:
                case Data.MotionInfo.DirectionValues.SE:
                case Data.MotionInfo.DirectionValues.SW:
                case Data.MotionInfo.DirectionValues.NW:
                    behavior = CreateMoveAngleFromMouseBehavior(game, owner, motionInfo, ConvertDirectionToRadians(motionInfo.Direction));
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a motion direction of '" + motionInfo.Direction.ToString() + "'.");
            }

            if (callReverse)
            {
                behavior.Reverse();
            }
            return behavior;
        }

        private static MotionBehavior CreateMoveTowardMouseXBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardMouseXAccelerationBehavior(game, owner, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardMouseXVelocityBehavior(game, owner, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardMouseXMaxSpeedBehavior(game, owner, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveTowardMouseYBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardMouseYAccelerationBehavior(game, owner, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardMouseYVelocityBehavior(game, owner, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardMouseYMaxSpeedBehavior(game, owner, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveAngleFromMouseBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo, double radians)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveAngleFromMouseAccelerationBehavior(game, owner, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveAngleFromMouseVelocityBehavior(game, owner, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveAngleFromMouseMaxSpeedBehavior(game, owner, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveRightFromMouseBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveRightFromMouseAccelerationBehavior(game, owner, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveRightFromMouseVelocityBehavior(game, owner, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveRightFromMouseMaxSpeedBehavior(game, owner, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveTowardMouseBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardMouseAccelerationBehavior(game, owner, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardMouseVelocityBehavior(game, owner, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardMouseMaxSpeedBehavior(game, owner, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateActorRelativeMoveBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            bool callReverse = false;
            MotionBehavior behavior;

            switch (motionInfo.Direction)
            {
                case Data.MotionInfo.DirectionValues.S:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.N;
                case Data.MotionInfo.DirectionValues.N:
                    behavior = CreateMoveTowardActorBehavior(game, owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.W:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.E;
                case Data.MotionInfo.DirectionValues.E:
                    behavior = CreateMoveRightFromActorBehavior(owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.NE:
                case Data.MotionInfo.DirectionValues.SE:
                case Data.MotionInfo.DirectionValues.SW:
                case Data.MotionInfo.DirectionValues.NW:
                    behavior = CreateMoveAngleFromActorBehavior(game, owner, motionInfo, ConvertDirectionToRadians(motionInfo.Direction));
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a motion direction of '" + motionInfo.Direction.ToString() + "'.");
            }

            if (callReverse)
            {
                behavior.Reverse();
            }
            return behavior;
        }

        private static MotionBehavior CreateMoveAngleFromActorBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo, double radians)
        {
            string actor = motionInfo.Includes.getIndexedKey(0);
            if (actor == null)
            {
                throw new ArgumentException("The actor instance '" + motionInfo.Includes.getIndexedKey(0) + "' doesn't exist in the current scene");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveAngleFromActorsAccelerationBehavior(game, owner, actor, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveAngleFromActorsVelocityBehavior(game, owner, actor, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveAngleFromActorsMaxSpeedBehavior(game, owner, actor, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveRightFromActorBehavior(BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            string actor = motionInfo.Includes.getIndexedKey(0);
            if (actor == null)
            {
                throw new ArgumentException("To move relative to an actor, an actor name must be specified");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveRightFromActorsAccelerationBehavior(owner, actor, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveRightFromActorsVelocityBehavior(owner, actor, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveRightFromActorsMaxSpeedBehavior(owner, actor, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveTowardActorBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo)
        {
            string actor = null;
            if (motionInfo.Includes.Count != 0)
            {
                actor = motionInfo.Includes.getIndexedKey(0);
            }
            else
            {
                throw new ArgumentException("Move toward actor behaviors must specify which actor to move toward.");
            }

            if (actor == null)
            {
                throw new ArgumentException("The actor instance '" + motionInfo.Includes.getIndexedKey(0) + "' doesn't exist in the current scene");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardActorsAccelerationBehavior(game, owner, actor, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardActorsVelocityBehavior(game, owner, actor, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardActorsMaxSpeedBehavior(game, owner, actor, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateActorInstanceRelativeMoveBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            bool callReverse = false;
            MotionBehavior behavior;
            
            switch (motionInfo.Direction)
            {
                case Data.MotionInfo.DirectionValues.S :
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.N;
                case Data.MotionInfo.DirectionValues.N:
                    behavior = CreateMoveTowardActorInstanceBehavior(game, owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.W:
                    callReverse = true;
                    goto case Data.MotionInfo.DirectionValues.E;
                case Data.MotionInfo.DirectionValues.E :
                    behavior = CreateMoveRightFromActorInstanceBehavior(owner, motionInfo);
                    break;
                case Data.MotionInfo.DirectionValues.NE:
                case Data.MotionInfo.DirectionValues.SE:
                case Data.MotionInfo.DirectionValues.SW:
                case Data.MotionInfo.DirectionValues.NW:
                    behavior = CreateMoveAngleFromActorInstanceBehavior(game, owner, motionInfo, ConvertDirectionToRadians(motionInfo.Direction));
                    break;
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a motion direction of '" + motionInfo.Direction.ToString() + "'.");
            }

            if (callReverse)
            {
                behavior.Reverse();
            }
            return behavior;
        }

        private static MotionBehavior CreateMoveAngleFromActorInstanceBehavior(Game game, BehaviorOwner owner, Microsoft.Popfly.GameCreator.Data.MotionInfo motionInfo, double radians)
        {
            Actor actor = owner.Scene.GetActor(motionInfo.Includes.getIndexedKey(0));
            if (actor == null)
            {
                throw new ArgumentException("The actor instance '" + motionInfo.Includes.getIndexedKey(0) + "' doesn't exist in the current scene");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveAngleFromActorAccelerationBehavior(game, owner, actor, motionInfo.Acceleration, radians);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveAngleFromActorVelocityBehavior(game, owner, actor, motionInfo.VelocityJump, radians);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveAngleFromActorMaxSpeedBehavior(game, owner, actor, motionInfo.MaxSpeed, radians);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveRightFromActorInstanceBehavior(BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            Actor actor = owner.Scene.GetActor(motionInfo.Includes.getIndexedKey(0));
            if (actor == null)
            {
                throw new ArgumentException("The actor instance '" + motionInfo.Includes.getIndexedKey(0) + "' doesn't exist in the current scene");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveRightFromActorAccelerationBehavior(owner, actor, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveRightFromActorVelocityBehavior(owner, actor, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveRightFromActorMaxSpeedBehavior(owner, actor, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        private static MotionBehavior CreateMoveTowardActorInstanceBehavior(Game game, BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            Actor actor = owner.Scene.GetActor(motionInfo.Includes.getIndexedKey(0));
            if (actor == null)
            {
                throw new ArgumentException("The actor instance '" + motionInfo.Includes.getIndexedKey(0) + "' doesn't exist in the current scene");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new MoveTowardActorAccelerationBehavior(game, owner, actor, motionInfo.Acceleration);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new MoveTowardActorVelocityBehavior(game, owner, actor, motionInfo.VelocityJump);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new MoveTowardActorMaxSpeedBehavior(game, owner, actor, motionInfo.MaxSpeed);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }


        public static MotionBehavior CreateXMove(BehaviorOwner owner, Data.MotionInfo motionInfo, Data.MotionInfo.ModifiesValues modifies, double modifier)
        {
            double value = 0;
            switch (modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    value = motionInfo.Acceleration * modifier;
                    return new SimpleXAccelerationMove(owner, value);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    value = motionInfo.VelocityJump * modifier;
                    return new SimpleXVelocityMove(owner, value);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    value = motionInfo.MaxSpeed * modifier;
                    return new SimpleXMaxSpeedMove(owner, value);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        modifies.ToString() + "'."
                        );
            }
        }

        public static MotionBehavior CreateYMove(BehaviorOwner owner, Data.MotionInfo motionInfo, Data.MotionInfo.ModifiesValues modifies, double modifier)
        {
            double value = 0;
            switch (modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    value = motionInfo.Acceleration * modifier;
                    return new SimpleYAccelerationMove(owner, value);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    value = motionInfo.VelocityJump * modifier;
                    return new SimpleYVelocityMove(owner, value);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    value = motionInfo.MaxSpeed * modifier;
                    return new SimpleYMaxSpeedMove(owner, value);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        modifies.ToString() + "'."
                        );
            }
        }

        public static MotionBehavior CreateXYMove(BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new SimpleXYAccelerationMove(owner, motionInfo.Acceleration, motionInfo.Direction);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new SimpleXYVelocityMove(owner, motionInfo.VelocityJump, motionInfo.Direction);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new SimpleXYMaxSpeedMove(owner, motionInfo.MaxSpeed, motionInfo.Direction);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        public static MotionBehavior CreateSimpleRotation(BehaviorOwner owner, Data.MotionInfo motionInfo, double modifier)
        {
            if (owner.IsActor == false)
            {
                throw new ArgumentException("Rotations are only supported on actors.");
            }
            switch (motionInfo.Modifies)
            {
                case Data.MotionInfo.ModifiesValues.Acceleration:
                    return new SimpleAccelerationRotation((Actor) owner, motionInfo.Acceleration * modifier);
                case Data.MotionInfo.ModifiesValues.Velocity:
                    return new SimpleVelocityRotation((Actor) owner, motionInfo.VelocityJump * modifier);
                case Data.MotionInfo.ModifiesValues.MaxSpeed:
                    return new SimpleMaxSpeedRotation((Actor) owner, motionInfo.MaxSpeed * modifier);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to handle a modify value of + '" +
                        motionInfo.Modifies.ToString() + "'."
                        );
            }
        }

        public static MotionBehavior CreateSimpleMotionBehavior(BehaviorOwner owner, Data.MotionInfo motionInfo)
        {
            switch (motionInfo.Type)
            {
                case "Move":
                    {
                        double modifier = 1.0;
                        switch (motionInfo.Direction)
                        {
                            case Data.MotionInfo.DirectionValues.N:
                            case Data.MotionInfo.DirectionValues.E:
                            case Data.MotionInfo.DirectionValues.S:
                            case Data.MotionInfo.DirectionValues.W:
                                if (motionInfo.Direction == Data.MotionInfo.DirectionValues.N ||
                                    motionInfo.Direction == Data.MotionInfo.DirectionValues.W)
                                {
                                    modifier = -1.0; ;
                                }


                                if (motionInfo.Direction == Data.MotionInfo.DirectionValues.N ||
                                    motionInfo.Direction == Data.MotionInfo.DirectionValues.S)
                                {
                                    return CreateYMove(owner, motionInfo, motionInfo.Modifies, modifier);
                                }
                                else
                                {
                                    return CreateXMove(owner, motionInfo, motionInfo.Modifies, modifier);
                                }
                            case Data.MotionInfo.DirectionValues.NE:
                            case Data.MotionInfo.DirectionValues.NW:
                            case Data.MotionInfo.DirectionValues.SE:
                            case Data.MotionInfo.DirectionValues.SW:
                                return CreateXYMove(owner, motionInfo);
                            default:
                                throw new NotImplementedException(
                                    Utils.ErrorMessagePrefix +
                                    "We have only implemented motions for directions N,S,E,W,NE,NW,SE,SW.  " +
                                    "Direction for this motion was specified as '" +
                                    motionInfo.Direction.ToString() +
                                    "'."
                                    );

                        }
                    }
                case "Rotate":
                    {
                        double modifier = 1.0;
                        switch (motionInfo.Direction)
                        {
                            case Data.MotionInfo.DirectionValues.E:
                                modifier = 1.0;
                                break;
                            case Data.MotionInfo.DirectionValues.W:
                                modifier = -1.0;
                                break;
                            default:
                                // Compat hack - if nothing's specified, rotate in the positive direction
                                modifier = 1.0;
                                Console.IssueWarning("No direction specified for rotation behavior, choosing clockwise.");
                                break;
                        }
                        return CreateSimpleRotation(owner, motionInfo, modifier);
                    }
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "We have only implemented motions that modify Movement and Rotation in CreateSimpleMoveBehavior.  " +
                        "A move type of '" + motionInfo.Type + "' was passed in."
                        );
            }
        }
    }
}
